Matching Market

This simple model consists of a buyer, a supplier, and a market.

The buyer represents a group of customers whose willingness to pay for a single unit of the good is captured by a vector of prices wta. You can initiate the buyer with a set_quantity function which randomly assigns the willingness to pay according to your specifications. You may ask for these willingness to pay quantities with a getbid function.

The supplier is similar, but instead the supplier is willing to be paid to sell a unit of technology. The supplier for instance may have non-zero variable costs that make them unwilling to produce the good unless they receive a specified price. Similarly the supplier has a get_ask function which returns a list of desired prices.

The willingness to pay or sell are set randomly using uniform random distributions. The resultant lists of bids are effectively a demand curve. Likewise the list of asks is effectively a supply curve. A more complex determination of bids and asks is possible, for instance using time of year to vary the quantities being demanded.

New in version 9

  • fixing initialization of seller_info
  • varying costs for seller, dependant on reservoir depeletion (price of exploration doubles)

Microeconomic Foundations

The market assumes the presence of an auctioneer which will create a book, which seeks to match the bids and the asks as much as possible. If the auctioneer is neutral, then it is incentive compatible for the buyer and the supplier to truthfully announce their bids and asks. The auctioneer will find a single price which clears as much of the market as possible. Clearing the market means that as many willing swaps happens as possible. You may ask the market object at what price the market clears with the get_clearing_price function. You may also ask the market how many units were exchanged with the get_units_cleared function.

Agent-Based Objects

The following section presents three objects which can be used to make an agent-based model of an efficient, two-sided market.


In [90]:
%matplotlib inline
import matplotlib.pyplot as plt
import random as rnd
import pandas as pd
import numpy as np
import time
import datetime
import calendar


# fix what is missing with the datetime/time/calendar package
def add_months(sourcedate,months):
    month = sourcedate.month - 1 + months
    year = int(sourcedate.year + month / 12 )
    month = month % 12 + 1
    day = min(sourcedate.day,calendar.monthrange(year, month)[1])
    return datetime.date(year,month,day)

classes buyers and sellers

Below we are constructing the buyers and sellers in classes.


In [103]:
# measure how long it takes to run the script
startit = time.time()
dtstartit = datetime.datetime.now()

class Seller():
    def __init__(self, name):
        self.name = name
        self.wta = []
        self.step = 0
        self.prod = 2000
        self.lb_price = 10
        self.lb_multiplier = 0
        self.ub_price = 20
        self.ub_multiplier = 0
        self.init_reserve = 500000
        self.reserve = 500000
        #multiple market idea, also 'go away from market'
        self.subscr_market = {}

    # the supplier has n quantities that they can sell
    # they may be willing to sell this quantity anywhere from a lower price of l
    # to a higher price of u
    def set_quantity(self):
        self.update_price()
        n = self.prod
        l = self.lb_price + self.lb_multiplier
        u = self.ub_price + self.ub_multiplier
        wta = []
        for i in range(n):
            p = rnd.uniform(l, u)
            wta.append(p)
        
        if len(wta) < self.reserve:
            self.wta = wta
        else:
            self.wta = wta[0:(self.reserve-1)]
            self.prod = self.reserve
        
    def get_name(self):
        return self.name

    def get_asks(self):
        return self.wta

    def extract(self, cur_extraction):
        if self.reserve > 0:
            self.reserve = self.reserve - cur_extraction
        else:
            self.prod = 0

    # production costs rise a 100% 
    def update_price(self):
        depletion = (self.init_reserve - self.reserve) / self.init_reserve
        self.ub_multiplier = int(self.ub_price * depletion)
        self.lb_multiplier = int(self.lb_price * depletion)


class Buyer():
    def __init__(self, name):
        self.name = name
        self.type = 0
        self.wtp = []
        self.step = 0
        self.base_demand = 0
        self.max_demand = 0
        self.lb_price = 10
        self.ub_price = 20

    # the supplier has n quantities that they can buy
    # they may be willing to sell this quantity anywhere from a lower price of l
    # to a higher price of u
    def set_quantity(self):
        n = int(self.consumption(self.step))
        l = self.lb_price
        u = self.ub_price
        wtp = []
        for i in range(n):
            p = rnd.uniform(l, u)
            wtp.append(p)
        self.wtp = wtp
        
    # gets a little to obvious
    def get_name(self):
        return self.name
    
    # return list of willingness to pay
    def get_bids(self):
        return self.wtp
    
    def consumption(self, x):
        # make it initialise to seller
        b = self.base_demand
        m = self.max_demand
        y = b + m * (.5 * (1 + np.cos((x/6)*np.pi)))
        return(y)
    
    def update_price(self):
        if self.type == 1: #home
            self.lb_price = 20
            self.ub_price = 40
        if self.type == 2: # elec
            self.lb_price = 10
            self.lb_price = 20
        if self.type == 3: #indu
            self.lb_price = 11
            self.ub_price = 21


  File "<ipython-input-103-859074d3036f>", line 97
    def update_price():
                       ^
SyntaxError: unexpected EOF while parsing

Construct the market

For the market two classes are made. The market itself, which controls the buyers and the sellers, and the book. The market has a book where the results of the clearing procedure are stored.


In [92]:
# the book is an object of the market used for the clearing procedure
class Book():
    def __init__(self):
        self.ledger = pd.DataFrame(columns = ("role","name","price","cleared"))

    def set_asks(self,seller_list):
        # ask each seller their name
        # ask each seller their willingness
        # for each willingness append the data frame
        for seller in seller_list:
            seller_name = seller.get_name()
            seller_price = seller.get_asks()
            for price in seller_price:
                self.ledger=self.ledger.append({"role":"seller","name":seller_name,"price":price,"cleared":"in process"},
                                               ignore_index=True)

    def set_bids(self,buyer_list):
        # ask each seller their name
        # ask each seller their willingness
        # for each willingness append the data frame
        for buyer in buyer_list:
            buyer_name = buyer.get_name()
            buyer_price = buyer.get_bids()
            for price in buyer_price:
                self.ledger=self.ledger.append({"role":"buyer","name":buyer_name,"price":price,"cleared":"in process"},
                                               ignore_index=True)

    def update_ledger(self,ledger):
        self.ledger = ledger
        
    def get_ledger(self):
        return self.ledger
    
    def clean_ledger(self):
        self.ledger = pd.DataFrame(columns = ("role","name","price","cleared"))

class Market():
    def __init__(self):
        self.count = 0
        self.last_price = ''
        self.book = Book()
        self.b = []
        self.s = []
        self.buyer_list = []
        self.seller_list = []
        self.buyer_dict = {}
        self.seller_dict = {}
        self.ledger = ''
    
    def update_seller(self):
        for i in self.seller_dict:
            self.seller_dict[i].step += 1
            self.seller_dict[i].set_quantity()

    def update_buyer(self):
        for i in self.buyer_dict:
            self.buyer_dict[i].step += 1
            self.buyer_dict[i].set_quantity()

    def add_buyer(self,buyer):
        self.b.append(buyer)
        self.buyer_list.append(buyer)
        
    def add_seller(self,seller):
        self.s.append(seller)        
        self.seller_list.append(seller)
    
    def set_book(self):
        self.book.set_bids(self.buyer_list)
        self.book.set_asks(self.seller_list)
    
    #def get_ledger(self):
    #    self.ledger = self.book.get_ledger()
    #    return self.ledger
    
    def get_bids(self):
        # this is a data frame
        ledger = self.book.get_ledger()
        rows= ledger.loc[ledger['role'] == 'buyer']
        # this is a series
        prices=rows['price']
        # this is a list
        bids = prices.tolist()
        return bids
    
    def get_asks(self):
        # this is a data frame
        ledger = self.book.get_ledger()
        rows = ledger.loc[ledger['role'] == 'seller']
        # this is a series
        prices=rows['price']
        # this is a list
        asks = prices.tolist()
        return asks
    
    # return the price at which the market clears
    # this fails because there are more buyers then sellers
    
    def get_clearing_price(self):
        # buyer makes a bid starting with the buyer which wants it most
        b = self.get_bids()
        s = self.get_asks()
        # highest to lowest
        self.b=sorted(b, reverse=True)
        # lowest to highest
        self.s=sorted(s, reverse=False)
        
        # find out whether there are more buyers or sellers
        # then drop the excess buyers or sellers; they won't compete
        n = len(b)
        m = len(s)
        
        # there are more sellers than buyers
        # drop off the highest priced sellers 
        if (m > n):
            s = s[0:n]
            matcher = n
        # There are more buyers than sellers
        # drop off the lowest bidding buyers 
        else:
            b = b[0:m]
            matcher = m
        
        # It's possible that not all items sold actually clear the market here
        count = 0
        for i in range(matcher):
            if (self.b[i] > self.s[i]):
                count +=1
                self.last_price = self.b[i]
        
        # copy count to market object
        self.count = count
        return self.last_price
    
    # TODO: Annotate the ledger
    def annotate_ledger(self,clearing_price):
        ledger = self.book.get_ledger()
        for index, row in ledger.iterrows():
            if (row['role'] == 'seller'):
                if (row['price'] < clearing_price):
                    ledger.loc[index,'cleared'] = 'True'
                else:
                    ledger.loc[index,'cleared'] = 'False'
            else:
                if (row['price'] > clearing_price):
                    ledger.loc[index,'cleared'] = 'True'
                else:
                    ledger.loc[index,'cleared'] = 'False'  
                    
        self.book.update_ledger(ledger)
    
    def get_units_cleared(self):
        return self.count
    
    def clean_ledger(self):
        self.ledger = ''
        self.book.clean_ledger()

    def run_it(self):
        self.pre_clearing_operation()
        self.clearing_operation()
        self.after_clearing_operation()
        
        #pre clearing empty out the last run and start
        # clean ledger is kind of sloppy, rewrite functions to overide the ledger
    def pre_clearing_operation(self):
        self.clean_ledger()
        self.update_buyer()
        self.update_seller()
        
    def clearing_operation(self):
        self.set_book()
        clearing_price = self.get_clearing_price()
        self.annotate_ledger(clearing_price)
        
    def after_clearing_operation(self):
        for i in self.seller_dict:
            name = self.seller_dict[i].name
            cur_extract = len(self.book.ledger[(self.book.ledger.cleared == 'True') &
                              (self.book.ledger.name == name)])
            self.seller_dict[i].extract(cur_extract)

Observer

The observer holds the clock and collects data. In this setup it tells the market another tick has past and it is time to act. The market will instruct the other agents. The observer initializes the model, thereby making real objects out of the classes defined above.


In [93]:
class Observer():
    def __init__(self, x, y, z):
        self.init_buyer = x
        self.init_seller = y
        self.maxrun = z
        self.hist_book = []
        self.buyer_dict = {}
        self.seller_dict = {}
        self.timetick = 0
        self.gas_market = ''
        self.reserve = []

    def set_buyer(self, buyer_info):
        for name in buyer_info:
            self.buyer_dict[name] = Buyer('%s' % name)
            self.buyer_dict[name].base_demand = buyer_info[name]['b']
            self.buyer_dict[name].max_demand = buyer_info[name]['m']
            self.buyer_dict[name].lb_price = buyer_info[name]['lb_price']
            self.buyer_dict[name].ub_price = buyer_info[name]['ub_price']

    def set_seller(self, seller_info):
        for name in seller_info:
            self.seller_dict[name] = Seller('%s' % name)
            self.seller_dict[name].prod = seller_info[name]['prod']
            self.seller_dict[name].lb_price = seller_info[name]['lb_price']
            self.seller_dict[name].ub_price = seller_info[name]['ub_price']
            self.seller_dict[name].reserve = seller_info[name]['reserve']
            self.seller_dict[name].init_reserve = seller_info[name]['reserve']
            
    def get_reserve(self):
        reserve = []
        for name in self.seller_dict:
            reserve.append(self.seller_dict[name].reserve)
        return reserve
    
    def set_market(self):
        self.gas_market = Market()
        #add suplliers and buyers to this market
        for supplier in self.seller_dict.values():
            self.gas_market.add_seller(supplier)
        for buyer in self.buyer_dict.values():
            self.gas_market.add_buyer(buyer)
        self.gas_market.seller_dict = self.seller_dict
        self.gas_market.buyer_dict = self.buyer_dict

    def run_it(self):
        # Timing
        # time initialising
        startit_init = time.time()
        
        #initialise, setting up all the agents
        first_run = True
        if first_run:
            self.set_buyer(self.init_buyer)
            self.set_seller(self.init_seller)
            self.set_market()
            first_run=False
            
        # time init stop
        stopit_init = time.time() - startit_init
        print('%s : init' % stopit_init)
        
        for period in range(self.maxrun):
            # time the period
            startit_period = time.time()

            self.timetick += 1
            print('#######################################')
            period_now = add_months(period_null, self.timetick-1)
            print(period_now.strftime('%Y-%b'))

            # real action on the market
            self.gas_market.run_it()

            # data collection
            p_clearing = self.gas_market.last_price
            q_sold = self.gas_market.count
            self.reserve.append([period_now.strftime('%Y-%b'),*self.get_reserve()])

            # recording the step_info
            # since this operation can take quite a while, print after every operation
            period_time = time.time() - startit_period
            print('%s : seconds to clear period' % period_time)
            self.hist_book.append([period_now.strftime('%Y-%b'), p_clearing, q_sold])

Example Market

In the following code example we use the buyer and supplier objects to create a market. At the market a single price is announced which causes as many units of goods to be swapped as possible. The buyers and sellers stop trading when it is no longer in their own interest to continue.


In [94]:
# Show some real consumption data, for more data see folder data analytics
#read montly consumption data of 2010 into a dataframe
df = pd.read_csv('2010cbstestrun.csv', header=0, index_col=0)
df = df.transpose()

#plot the 2010 monthly consumption data
df.plot();
df


Out[94]:
elec indu home
jan 1066 1572 4218
feb 941 1349 3490
mar 965 1416 2636
apr 841 1215 1614
may 742 1285 1458
jun 673 1171 763
jul 698 1229 603
aug 668 1169 709
sep 729 1207 1042
okt 983 1362 1742
nov 944 1371 2632
dec 994 1505 4301

In [95]:
# make initialization dictionary
init_buyer = {'elec':{'b':400, 'm' : 673, 'lb_price': 10, 'ub_price' : 20},
              'indu':{'b':400, 'm':1171, 'lb_price': 10, 'ub_price' : 20},
              'home':{'b': 603, 'm': 3615, 'lb_price': 10, 'ub_price' : 20}}
init_seller = {'NL' : {'prod': 2000, 'lb_price': 10, 'ub_price' : 20, 'reserve': 50000},
               'RU' : {'prod': 2000, 'lb_price': 15, 'ub_price' : 30, 'reserve': 500000}}

# make a history book to record every timestep
hist_book = []

# set the starting time
period_null= datetime.date(2010,1,1)

run the model

To run the model we create the observer. The observer creates all the other objects and runs the model.


In [96]:
# create observer and run the model
# first data about buyers then sellers and then model ticks
years = 10
timestep = 12

obser1 = Observer(init_buyer, init_seller, years*timestep)
obser1.run_it()
#get the info from the observer
hist_book = obser1.hist_book


0.0010006427764892578 : init
#######################################
2010-Jan
32.318824768066406 : seconds to clear period
#######################################
2010-Feb
28.395031213760376 : seconds to clear period
#######################################
2010-Mar
23.485578298568726 : seconds to clear period
#######################################
2010-Apr
19.142528533935547 : seconds to clear period
#######################################
2010-May
16.030311584472656 : seconds to clear period
#######################################
2010-Jun
15.246767044067383 : seconds to clear period
#######################################
2010-Jul
16.427579402923584 : seconds to clear period
#######################################
2010-Aug
19.399693489074707 : seconds to clear period
#######################################
2010-Sep
23.8378267288208 : seconds to clear period
#######################################
2010-Oct
28.16289210319519 : seconds to clear period
#######################################
2010-Nov
31.661348581314087 : seconds to clear period
#######################################
2010-Dec
32.86019563674927 : seconds to clear period
#######################################
2011-Jan
31.39715051651001 : seconds to clear period
#######################################
2011-Feb
28.026801109313965 : seconds to clear period
#######################################
2011-Mar
23.466559171676636 : seconds to clear period
#######################################
2011-Apr
19.128507137298584 : seconds to clear period
#######################################
2011-May
16.058335542678833 : seconds to clear period
#######################################
2011-Jun
14.93954086303711 : seconds to clear period
#######################################
2011-Jul
16.010301113128662 : seconds to clear period
#######################################
2011-Aug
19.142500162124634 : seconds to clear period
#######################################
2011-Sep
23.465563774108887 : seconds to clear period
#######################################
2011-Oct
28.062822103500366 : seconds to clear period
#######################################
2011-Nov
31.47521734237671 : seconds to clear period
#######################################
2011-Dec
32.73310565948486 : seconds to clear period
#######################################
2012-Jan
31.45120072364807 : seconds to clear period
#######################################
2012-Feb
28.125853538513184 : seconds to clear period
#######################################
2012-Mar
23.511584281921387 : seconds to clear period
#######################################
2012-Apr
19.016435861587524 : seconds to clear period
#######################################
2012-May
15.965270757675171 : seconds to clear period
#######################################
2012-Jun
14.887512922286987 : seconds to clear period
#######################################
2012-Jul
16.006298542022705 : seconds to clear period
#######################################
2012-Aug
19.127502918243408 : seconds to clear period
#######################################
2012-Sep
23.472567081451416 : seconds to clear period
#######################################
2012-Oct
28.0257830619812 : seconds to clear period
#######################################
2012-Nov
31.468213081359863 : seconds to clear period
#######################################
2012-Dec
32.84618520736694 : seconds to clear period
#######################################
2013-Jan
31.403167247772217 : seconds to clear period
#######################################
2013-Feb
27.977749347686768 : seconds to clear period
#######################################
2013-Mar
23.477571964263916 : seconds to clear period
#######################################
2013-Apr
19.13050413131714 : seconds to clear period
#######################################
2013-May
16.074334859848022 : seconds to clear period
#######################################
2013-Jun
14.919543504714966 : seconds to clear period
#######################################
2013-Jul
16.052319049835205 : seconds to clear period
#######################################
2013-Aug
19.09649348258972 : seconds to clear period
#######################################
2013-Sep
23.553624629974365 : seconds to clear period
#######################################
2013-Oct
27.95673418045044 : seconds to clear period
#######################################
2013-Nov
31.409170627593994 : seconds to clear period
#######################################
2013-Dec
32.718095779418945 : seconds to clear period
#######################################
2014-Jan
31.43118667602539 : seconds to clear period
#######################################
2014-Feb
28.016764879226685 : seconds to clear period
#######################################
2014-Mar
23.53562569618225 : seconds to clear period
#######################################
2014-Apr
19.125505208969116 : seconds to clear period
#######################################
2014-May
16.05233073234558 : seconds to clear period
#######################################
2014-Jun
14.929521560668945 : seconds to clear period
#######################################
2014-Jul
15.978290557861328 : seconds to clear period
#######################################
2014-Aug
19.080456495285034 : seconds to clear period
#######################################
2014-Sep
23.485578060150146 : seconds to clear period
#######################################
2014-Oct
28.2519428730011 : seconds to clear period
#######################################
2014-Nov
31.466223001480103 : seconds to clear period
#######################################
2014-Dec
32.750118255615234 : seconds to clear period
#######################################
2015-Jan
31.59330105781555 : seconds to clear period
#######################################
2015-Feb
27.947727918624878 : seconds to clear period
#######################################
2015-Mar
23.438544511795044 : seconds to clear period
#######################################
2015-Apr
19.11348009109497 : seconds to clear period
#######################################
2015-May
16.07836604118347 : seconds to clear period
#######################################
2015-Jun
14.900501251220703 : seconds to clear period
#######################################
2015-Jul
16.075359344482422 : seconds to clear period
#######################################
2015-Aug
19.075464963912964 : seconds to clear period
#######################################
2015-Sep
23.54161763191223 : seconds to clear period
#######################################
2015-Oct
28.058794498443604 : seconds to clear period
#######################################
2015-Nov
31.46822452545166 : seconds to clear period
#######################################
2015-Dec
32.84517312049866 : seconds to clear period
#######################################
2016-Jan
31.459206342697144 : seconds to clear period
#######################################
2016-Feb
28.0658278465271 : seconds to clear period
#######################################
2016-Mar
23.490565061569214 : seconds to clear period
#######################################
2016-Apr
19.18655562400818 : seconds to clear period
#######################################
2016-May
16.060336351394653 : seconds to clear period
#######################################
2016-Jun
14.97056770324707 : seconds to clear period
#######################################
2016-Jul
16.07033133506775 : seconds to clear period
#######################################
2016-Aug
19.099494218826294 : seconds to clear period
#######################################
2016-Sep
23.449540376663208 : seconds to clear period
#######################################
2016-Oct
28.05481767654419 : seconds to clear period
#######################################
2016-Nov
31.54126238822937 : seconds to clear period
#######################################
2016-Dec
32.8351776599884 : seconds to clear period
#######################################
2017-Jan
31.564285278320312 : seconds to clear period
#######################################
2017-Feb
28.340004920959473 : seconds to clear period
#######################################
2017-Mar
23.47656750679016 : seconds to clear period
#######################################
2017-Apr
19.106486320495605 : seconds to clear period
#######################################
2017-May
16.027312994003296 : seconds to clear period
#######################################
2017-Jun
14.93954610824585 : seconds to clear period
#######################################
2017-Jul
16.047327280044556 : seconds to clear period
#######################################
2017-Aug
19.101495027542114 : seconds to clear period
#######################################
2017-Sep
23.551613092422485 : seconds to clear period
#######################################
2017-Oct
28.095844268798828 : seconds to clear period
#######################################
2017-Nov
31.637127161026 : seconds to clear period
#######################################
2017-Dec
34.45332932472229 : seconds to clear period
#######################################
2018-Jan
32.08549952507019 : seconds to clear period
#######################################
2018-Feb
28.36001420021057 : seconds to clear period
#######################################
2018-Mar
23.730750799179077 : seconds to clear period
#######################################
2018-Apr
19.346638202667236 : seconds to clear period
#######################################
2018-May
16.151384353637695 : seconds to clear period
#######################################
2018-Jun
15.082646608352661 : seconds to clear period
#######################################
2018-Jul
16.446819305419922 : seconds to clear period
#######################################
2018-Aug
19.446715116500854 : seconds to clear period
#######################################
2018-Sep
23.766300201416016 : seconds to clear period
#######################################
2018-Oct
28.57552194595337 : seconds to clear period
#######################################
2018-Nov
32.03561282157898 : seconds to clear period
#######################################
2018-Dec
33.07134938240051 : seconds to clear period
#######################################
2019-Jan
31.973565340042114 : seconds to clear period
#######################################
2019-Feb
28.444077730178833 : seconds to clear period
#######################################
2019-Mar
24.021894216537476 : seconds to clear period
#######################################
2019-Apr
19.42171001434326 : seconds to clear period
#######################################
2019-May
16.08535408973694 : seconds to clear period
#######################################
2019-Jun
15.421903371810913 : seconds to clear period
#######################################
2019-Jul
16.514652252197266 : seconds to clear period
#######################################
2019-Aug
19.51577615737915 : seconds to clear period
#######################################
2019-Sep
23.774787187576294 : seconds to clear period
#######################################
2019-Oct
28.952818393707275 : seconds to clear period
#######################################
2019-Nov
31.643353700637817 : seconds to clear period
#######################################
2019-Dec
32.914228439331055 : seconds to clear period

In [97]:
# recording the total run
def write_to_csv(hist_book):
    f = open('hist_book.csv', 'a')
    for item in hist_book:
        f.write('%s,%s\n' % (item[0], item[1]))
    f.close()

#write_to_csv(hist_book)
    
# make a dataframe of clearing prices
df_hb = pd.DataFrame(hist_book)
df_hb = df_hb.set_index(0)
df_hb.index.name = 'month'
df_hb.rename(columns={1: 'price', 2: 'quantity'}, inplace=True)

Operations Research Formulation

The market can also be formulated as a very simple linear program or linear complementarity problem. It is clearer and easier to implement this market clearing mechanism with agents. One merit of the agent-based approach is that we don't need linear or linearizable supply and demand function.

The auctioneer is effectively following a very simple linear program subject to constraints on units sold. The auctioneer is, in the primal model, maximizing the consumer utility received by customers, with respect to the price being paid, subject to a fixed supply curve. On the dual side the auctioneer is minimizing the cost of production for the supplier, with respect to quantity sold, subject to a fixed demand curve. It is the presumed neutrality of the auctioneer which justifies the honest statement of supply and demand.

An alternative formulation is a linear complementarity problem. Here the presence of an optimal space of trades ensures that there is a Pareto optimal front of possible trades. The perfect opposition of interests in dividing the consumer and producer surplus means that this is a zero sum game. Furthermore the solution to this zero-sum game maximizes societal welfare and is therefore the Hicks optimal solution.

Next Steps

A possible addition of this model would be to have a weekly varying demand of customers, for instance caused by the use of natural gas as a heating agent. This would require the bids and asks to be time varying, and for the market to be run over successive time periods. A second addition would be to create transport costs, or enable intermediate goods to be produced. This would need a more elaborate market operator. Another possible addition would be to add a profit maximizing broker. This may require adding belief, fictitious play, or message passing.

The object-orientation of the models will probably need to be further rationalized. Right now the market requires very particular ordering of calls to function correctly.


In [98]:
# timeit

stopit = time.time()
dtstopit = datetime.datetime.now()

print('it took us %s seconds to get to this conclusion' % (stopit-startit))
print('in another notation (h:m:s) %s'% (dtstopit - dtstartit))


it took us 2855.3678278923035 seconds to get to this conclusion
in another notation (h:m:s) 0:47:35.367828

In [99]:
# print the run results
price = df_hb['price']
fig = price.plot()
plt.ylabel('€ / unit')
plt.show()

quantity = df_hb['quantity']
fig = quantity.plot()
plt.ylabel('quantity')
plt.show()


Time of last run

Time and date of the last run of this notebook file


In [100]:
# print the time of last run
print('last run of this notebook:')
time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())


last run of this notebook:
Out[100]:
'Mon, 10 Jul 2017 19:37:34'

In [101]:
#df_hb
df_res = pd.DataFrame(obser1.reserve, columns=['time', *[i for i in init_seller]])
df_res = df_res.set_index('time')
df_res.plot();



In [102]:
df_res['NL']


Out[102]:
time
2010-Jan    48573
2010-Feb    47171
2010-Mar    45943
2010-Apr    44889
2010-May    44103
2010-Jun    43393
2010-Jul    42614
2010-Aug    41652
2010-Sep    40657
2010-Oct    39572
2010-Nov    38576
2010-Dec    37582
2011-Jan    36608
2011-Feb    35746
2011-Mar    34917
2011-Apr    34286
2011-May    33768
2011-Jun    33323
2011-Jul    32848
2011-Aug    32276
2011-Sep    31652
2011-Oct    30957
2011-Nov    30203
2011-Dec    29445
2012-Jan    28783
2012-Feb    28198
2012-Mar    27667
2012-Apr    27206
2012-May    26841
2012-Jun    26530
            ...  
2017-Jul    10894
2017-Aug    10768
2017-Sep    10588
2017-Oct    10352
2017-Nov    10143
2017-Dec     9926
2018-Jan     9784
2018-Feb     9690
2018-Mar     9590
2018-Apr     9528
2018-May     9499
2018-Jun     9494
2018-Jul     9463
2018-Aug     9393
2018-Sep     9304
2018-Oct     9174
2018-Nov     9043
2018-Dec     8905
2019-Jan     8764
2019-Feb     8624
2019-Mar     8520
2019-Apr     8445
2019-May     8429
2019-Jun     8415
2019-Jul     8398
2019-Aug     8337
2019-Sep     8239
2019-Oct     8115
2019-Nov     7988
2019-Dec     7859
Name: NL, Length: 120, dtype: int64

In [ ]: